#include <user/ulib.h>

/* compiling time check for size of struct pipe_t */
static char __pipe_size[sizeof(struct pipe_t) == PAGE_SIZE ? 1 : -1];

static int pipe_write_unsafe(struct pipe_t *pipe, const void *buf, int n);
static int pipe_read_unsafe(struct pipe_t *pipe, void *buf, int n);

void
pipe_block_write(struct pipe_t *pipe, const void *buf, int n)
{
	 // kprintf("[pipe_block_write] %d: pipe = %08x, buf = %08x, n = %d\n", self_idx, va2pa(pipe), buf, n);
	 int k;
	 k = 0;
	 while (k < n)
	 {
		  k += pipe_write(pipe, (const char *)buf + k, n - k); 
		  if (k < n) {
			   wait();
		  }
	 }
	 // kprintf("[pipe_block_write] %d: done\n", self_idx);
}

void
pipe_block_read(struct pipe_t *pipe, void *buf, int n)
{
	 // kprintf("[pipe_block_read] %d: pipe = %08x, buf = %08x, n = %d\n", self_idx, va2pa(pipe), buf, n);
	 int k;
	 k = 0;
	 while (k < n)
	 {
		  k += pipe_read(pipe, (char *)buf + k, n - k); 
		  if (k < n) wait();
	 }
	 // kprintf("[pipe_block_read] %d: done\n", self_idx);

}

int
pipe_write(struct pipe_t *pipe, const void *buf, int n)
{
	 // mutex_lock(&pipe->lock);
	 // kprintf("[pipe_write] size = %d\n", pipe_size_unsafe(pipe));
	 int result = 0, i;
	 
	 while ((i = pipe_write_unsafe(pipe, (const char *)buf + result, n - result)) > 0)
		  result += i;

	 // mutex_unlock(&pipe->lock);

	 return result;
}

int
pipe_write_unsafe(struct pipe_t *pipe, const void *buf, int n)
{
	 int result;
	 
	 if (pipe->tail <= pipe->head)
	 {
		  result = pipe->head - pipe->tail;
		  if (result > n) result = n;
	 }
	 else
	 {
		  result = PIPE_BUFSIZE - pipe->tail;
		  if (result > n) result = n;
	 }

	 if (result != 0)
	 {
		  memmove(pipe->buf + pipe->tail, (const char *)buf, result);
		  pipe->tail += result;
		  if (pipe->tail >= PIPE_BUFSIZE)
			   pipe->tail -= PIPE_BUFSIZE;
	 }
	 
	 return result;
}

int
pipe_read(struct pipe_t *pipe, void *buf, int n)
{
	 mutex_lock(&pipe->lock);
	 // kprintf("[pipe_read] size = %d\n", pipe_size_unsafe(pipe));
	 
	 int result = 0, i;
	 
	 while ((i = pipe_read_unsafe(pipe, (char *)buf + result, n - result)) > 0)
		  result += i;

	 mutex_unlock(&pipe->lock);
	 return result;
}

int
pipe_read_unsafe(struct pipe_t *pipe, void *buf, int n)
{
	 int result;
	 int head = (pipe->head + 1) % PIPE_BUFSIZE;
	 
	 if (pipe->tail < head)
	 {
		  result = PIPE_BUFSIZE - head;
		  if (result > n) result = n;
	 }
	 else
	 {
		  result = pipe->tail - head;
		  if (result > n) result = n;
	 }

	 if (result != 0)
	 {
		  memmove((char *)buf, pipe->buf + head, result);
		  pipe->head += result;
		  if (pipe->head >= PIPE_BUFSIZE)
			   pipe->head -= PIPE_BUFSIZE;
	 }
	 
	 return result;
}

int
pipe_size_unsafe(struct pipe_t *pipe)
{
	 return (pipe->tail - pipe->head - 1 + PIPE_BUFSIZE) % PIPE_BUFSIZE;
}


int
pipe_size(struct pipe_t *pipe)
{
	 mutex_lock(&pipe->lock);
	 int result = pipe_size_unsafe(pipe);
	 mutex_unlock(&pipe->lock);

	 return result;
}

void
pipe_clear(struct pipe_t *pipe, int force)
{
	 if (!force)
		  mutex_lock(&pipe->lock);
	 pipe->head = PIPE_BUFSIZE - 1;
	 pipe->tail = 0;
	 if (!force) 
		  mutex_unlock(&pipe->lock);
	 else mutex_init(&pipe->lock);
}
	 

struct pipe_t *
pipe_new(void)
{
	 // 分配地址空间
	 struct pipe_t *result = palloc();
	 
	 alloc_page(result, PTE_W | PTE_U);
	 
	 mutex_init(&result->lock);
	 result->head = PIPE_BUFSIZE - 1;
	 result->tail = 0;
	 
	 return result;
}

uint32_t
pipe_share(struct pipe_t *pipe)
{
	 return share_page(pipe);
}

struct pipe_t *
pipe_open(uint32_t handler)
{
	 struct pipe_t *result = palloc();

	 // kprintf("[pipe_open] %d: result = %08x\n", self_idx, result);
	 
	 shared_map(result, handler | PTE_U | PTE_W);

	 // kprintf("[pipe_open] %d: open pipe %08x to %08x\n", self_idx, handler, result);
	 
	 return result;
}

void
pipe_close(struct pipe_t *pipe)
{
	 remove_map(pipe);
	 pfree(pipe);
}
